// 一、将下面异步代码使用 Promise 的方式改进
// setTimeout(function() {
//   var a = 'hello'
//   setTimeout(function() {
//       var b = 'lagou'
//       setTimeout(function() {
//           var c = 'I ❤️ U'
//           console.log(a + b + c)
//       }, 10);
//   }, 10);
// }, 10);



let p = function () {
  return new Promise(function (resolve, reject) { 
    let a = 'hello'
    resolve(a)
  }).then(a => {
    let b = 'lagou'
    return a+b
  })
  .then(ab => {
    let c = 'I ❤️ U'
    console.log(ab + c)
  })
}
p()


// 二、基于以下代码完成下面的四个练习
const { first } = require('lodash')
const fp = require('lodash/fp')
// 数据：horsepower 马力，dollar_value 价格，in_stock 库存
const cars = [
  { name: 'Ferrari FF', horsepower: 660, dollar_value: 700000, in_stock: true },
  { name: 'Spyker C12 Zagato', horsepower: 650, dollar_value: 648000, in_stock: false },
  { name: 'Jaguar XKR-S', horsepower: 550, dollar_value: 132000, in_stock: false },
  { name: 'Audi R8', horsepower: 525, dollar_value: 114200, in_stock: false },
  { name: 'Aston Martin One-77', horsepower: 750, dollar_value: 185000, in_stock: true },
  { name: 'Pagani Huayra', horsepower: 700, dollar_value: 130000, in_stock: false }
]

// 练习1：使用组合函数 fp.flowRight() 重新实现下面这个函数
// let isLastInStock = function(cars){
//   // 获取最后一条数据
//   let last_car = fp.last(cars)
//   // 获取最后一条数据的 in_stock 属性值
//   return fp.prop('in_stock', last_car)
// }
// 先定义获取最后一条数据的函数，再定义获取某个对象中的 in_stock 属性的函数，再用 fp.flowRight 组合函数
const isLastInStock = fp.flowRight(fp.prop('in_stock'),fp.last)
console.log(isLastInStock(cars))

// 练习2：使用 fp.flowRight()、fp.prop() 和 fp.first() 获取第一个 car 的 name
// 先定义获取第一条数据的函数，再定义获取某个对象中的 name 属性的函数，再用 fp.flowRight 组合函数
const isFirstName = fp.flowRight(fp.prop('name'),fp.first)
console.log(isFirstName(cars))

// 练习3：使用帮助函数 _average 重构 averageDollarValue，使用函数组合的方式实现
let _average = function(xs){
  return fp.reduce(fp.add, 0, xs) / xs.length
}
// 先定义获取某个对象中的 dollar_value 属性的函数，将该函数作为 fp.map 的数组元素处理函数，再用 fp.flowRight 组合函数
const averageDollarValue = fp.flowRight(_average,fp.map(fp.prop('dollar_value')))
console.log(averageDollarValue(cars))

// 练习4：使用 flowRight 写一个 sanitizeNames() 函数，返回一个下划线连续的小写字符串，把数组中的 name 转换为这种形式，例如：sanitizeNames(["Hello World"]) => ["hello_world"]
let _underscore = fp.replace(/\W+/g, '_') // 无须改动，并在 sanitizeNames 中使用它

// 先定义获取某个对象中的 name 属性的函数，再定义转化为小写的函数，再将空格和下划线替换，,再用 fp.flowRight 组合函数
const sanitizeNames = fp.flowRight( _underscore,fp.flowRight(fp.toLower,fp.map(fp.prop('name'))) )
console.log(sanitizeNames(cars))

// 三、基于下面提供的代码，完成后续的四个练习
// support.js

// 练习1：使用 fp.add(x, y) 和 fp.map(f,x) 创建一个能让 functor 里的值增加的函数 ex1
// const fp = require('lodash/fp')
const {Maybe, Container} = require('./support')
let maybe = Maybe.of([5,6,1])
let ex1 = () => {
  // 函子对象的 map 方法可以运行一个函数对值进行处理，函数的参数为传入 of 方法的参数；接着对传入的整个数组进行遍历，并对每一项执行 fp.add 方法
  return maybe.map(x => fp.map(fp.add(1), x))
}
console.log(ex1())


// 练习2：实现一个函数 ex2，能够使用 fp.first 获取列表的第一个元素
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {
 return xs.map(x => fp.first(x))
}
console.log(ex2())


// 练习3：实现一个函数 ex3，使用 safeProp 和 fp.first 找到 user 的名字的首字母
let safeProp = fp.curry(function(x, o){
    return Maybe.of(o[x])
})
let user = { id: 2, name: 'Albert' }
let ex3 = user => {
  return safeProp('name',user).map(name => fp.first(name))
}
console.log(ex3(user))
// 调用 ex3 函数传入 user 对象，safeProp 是经过柯里化处理的，可以先传“属性”参数，后传“对象”参数。safeProp 函数处理后返回 user 的值，再调用fp.first 获取首字母


// 练习4：使用 Maybe 重写 ex4，不要有 if 语句
// let ex4 = function(n){
//     if(n){
//         return parseInt(n)
//     }
// }
let ex4 = n => Maybe.of(n).map(n => parseInt(n))
console.log(ex4(null))
// MayBe 函子用来处理外部的空值情况，防止空值的异常，拿到函子的值之后进行 parseInt 转化


// 手写Promise
/**
 * 1.promise就是一个类，在执行这个类的时候，需要传递一个执行器进去，执行器会立即执行
 * 2.promise 中有三种状态，pending 等待，fulfilled 成功，rejected 失败
 * pending -> fulfilled
 * pending -> rejected
 * 一旦状态确定了，就不可更改
 * 3.resolve 和 reject 是用来更改状态的，resolve是将状态变为成功 fulfilled，reject是将状态变为失败 rejected
 * 4.then方法内部做的事情就是判断状态，如果状态是成功，调用成功的函数，如果是失败，调用失败的函数，then方法是被定义在原型对象中的
 * 5.then成功回调之后有一个参数,表示成功之后的值 then失败回调有一个参数，表示失败后的原因
 * 6.同一个promise对象下面的方法是可以被调用多次的
 * 7.then 方法是可以被链式调用的，后面then方法的回到函数拿到值的是上一个then方法的回调函数的返回值
 */
const PENDING = 'pending'  //等待的状态
const FULFILLED = 'fulfilled'  //成功状态
const REJECTED = 'rejected'  //失败状态
class MyPromise {
  constructor (executor) {
    try {
      executor(this.resolve,valuethis.reject)
    } catch (e) {
      this.reject(e)
    }
  }
  // promise状态
  status = PENDING;
  // 成功之后的值
  value = undefined
  // 失败的原因
  reason = undefined
  // 成功回调，存储多个回调函数
  successCallback = []
  // 失败回调，存储多个回调函数
  failCallback = []
  resolve = value => {
    // 如果状态不是等待，就阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态变为成功
    this.status = FULFILLED
    // 保存成功之后的值
    this.value = value
    // 判断成功回调是否存在，存在就调用
    while(this.successCallback.lenght) this.successCallback.shift()()
  }
  reject = reason => {
    // 如果状态不是等待，就阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态变为失败
    this.status = REJECTED
    // 保存失败的原因
    this.reason = reason
    // 判断失败回调是否存在，存在就调用
    while(this.failCallback.length) this.failCallback.shift()()
  }
  then (successCallback, failCallback) {
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => { throw reason}
    // 因为可以被链式调用，所以要返回一个promise对象
		let promise2 = new MyPromise((resolve, reject) => {
			//判断状态
			if (this.status === FULFILLED) { 
				setTimeout(() => {
					try {
						let x = successCallback(this.value)
						// 判断 x 的值是普通值还是promise对象
						// 如果是普通值, 直接调用resolve
						// 如果是promise对象， 查看promise对象返回的结果
						// 再根据promise对象返回的结果，决定调用resolve 还是调用 reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				},0)
			} else if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						let x = failCallback(this.reason)
						// 判断 x 的值是普通值还是promise对象
						// 如果是普通值, 直接调用resolve
						// 如果是promise对象， 查看promise对象返回的结果
						// 再根据promise对象返回的结果，决定调用resolve 还是调用 reject
						resolvePromise(promise2, x, resolve, reject)
					} catch (e) {
						reject(e)
					}
				},0)
			} else {
				// 等待
				// 将成功回调和失败回调存储下来
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							let x = successCallback(this.value)
							// 判断 x 的值是普通值还是promise对象
							// 如果是普通值, 直接调用resolve
							// 如果是promise对象， 查看promise对象返回的结果
							// 再根据promise对象返回的结果，决定调用resolve 还是调用 reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					},0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							let x = failCallback(this.reason)
							// 判断 x 的值是普通值还是promise对象
							// 如果是普通值, 直接调用resolve
							// 如果是promise对象， 查看promise对象返回的结果
							// 再根据promise对象返回的结果，决定调用resolve 还是调用 reject
							resolvePromise(promise2, x, resolve, reject)
						} catch (e) {
							reject(e)
						}
					},0)
				})
			}
    })
    return promise2
  }
  finally (callback) {
		return this.then(value => {
			return MyPromise.resolve(callback()).then(() => value)
		}, reason => {
			return MyPromise.resolve(callback()).then(() => {throw reason})
		})
	}
	catch (failCallback) {
		return this.then(undefined, failCallback)
	}
	static all (array) {
		let result = []
		let index = 0
		return new MyPromise((resolve, reject) => {
			function addData (key, value) {
				result[key] = value
        index++
        // 只有当计数index和数组的个数相等的时候，才是全部执行完了
				if (index === array.length) {
					resolve(result)
				}
			}
			for (let i = 0; i < array.length; i++) {
				let current = array[i]
				if (current instanceof MyPromise) {
					// Promise对象
					current.then(value => addData(i, value), reason => reject(reason))
				} else {
					// 普通值
					addData(i, array[i])
				}
			}
		})
  }
  static resolve (value) {
		if (value instanceof MyPromise) return value
		return new MyPromise(resolve => resolve(value))
	}
}

function resolvePromise(promise2, x, resolve, reject) {
	if (promise2 === x) {
		return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
	}
	if (x instanceof MyPromise) {
		// promise 对象
		x.then(resolve,reject)
	} else {
		// 普通值
		resolve(x)
	}
}

module.export = MyPromise